package sku

import (
	"context"
	"gorm.io/gorm"
	"mall/common/excption"
	constans "mall/services/product/rpc/consts"
	"mall/services/product/rpc/internal/domain/bo"
	"mall/services/product/rpc/internal/mapper"
	"mall/services/product/rpc/internal/mapper/entity"
	"time"

	"mall/services/product/rpc/internal/svc"
	"mall/services/product/rpc/pb"

	"github.com/zeromicro/go-zero/core/logx"
)

type SaveSkuLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewSaveSkuLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveSkuLogic {
	return &SaveSkuLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *SaveSkuLogic) SaveSku(in *pb.SaveSkuDTO) (*pb.SaveSkuVO, error) {
	// 1.query check sku
	skuMapper, checkError := l.checkRequestDTO(in)
	if checkError != nil {
		return nil, checkError
	}
	var idArr []int64
	// 2.begin transaction
	saveErr, idArr := l.saveDB(in, skuMapper, idArr)
	if saveErr != nil {
		return nil, saveErr
	}
	// 3.return obj
	return &pb.SaveSkuVO{SkuIdArr: idArr}, nil
}

func (l *SaveSkuLogic) saveDB(in *pb.SaveSkuDTO, skuMapper *mapper.SkuMapper, idArr []int64) (error, []int64) {
	saveErr := l.svcCtx.DBEngin.Transaction(func(tx *gorm.DB) error {
		// 2.1.save sku
		attrValueMap, skuIdArr, skuBatchInsertErr := l.skuBatchInsert(in, skuMapper)
		if skuBatchInsertErr != nil {
			return skuBatchInsertErr
		}
		// 2.2.builderAttrValueAndStock
		skuStoreArr, spuSkuAttrArr := l.builderAttrValueAndStock(attrValueMap, in)
		stockMapper := mapper.NewSkuStockMapper()
		_, skuBatchInsertErr = stockMapper.BatchInsertCtx(l.ctx, l.svcCtx.DBEngin, skuStoreArr)
		if skuBatchInsertErr != nil {
			return skuBatchInsertErr
		}
		// 2.3.save sku attr
		spuSkuAttrValueMapper := mapper.NewSpuSkuAttrValueMapper()
		_, skuBatchInsertErr = spuSkuAttrValueMapper.BatchInsertCtx(l.ctx, l.svcCtx.DBEngin, spuSkuAttrArr)
		if skuBatchInsertErr != nil {
			return skuBatchInsertErr
		}
		// 2.4.skuIdArr
		idArr = skuIdArr
		return nil
	})
	if saveErr != nil {
		return saveErr, nil
	}
	return nil, idArr
}

func (l *SaveSkuLogic) checkRequestDTO(in *pb.SaveSkuDTO) (*mapper.SkuMapper, error) {
	skuMapper := mapper.NewSkuMapper()
	skuBO := bo.SkuBO{}
	skuBO.SpuId = in.SpuId
	var skuNameArr []string
	for _, v := range in.SkuArr {
		skuNameArr = append(skuNameArr, v.SkuName)
	}
	skuBO.SkuNameArr = skuNameArr
	skuBO.NeStatus = constans.SpuTakeDown
	selectCountCtx, err := skuMapper.SelectCountCtx(l.ctx, l.svcCtx.DBEngin, skuBO)
	if err != nil {
		return nil, err
	}
	if selectCountCtx > 0 {
		return nil, excption.NewErrCode(constans.SkuNameRepeatError)
	}
	return skuMapper, nil
}

func (l *SaveSkuLogic) builderAttrValueAndStock(attrValueMap map[*entity.SkuEntity][]*pb.SpuSkuAttrValueDTO,
	in *pb.SaveSkuDTO) ([]entity.SkuStockEntity, []entity.SpuSkuAttrValueEntity) {

	var skuStoreArr []entity.SkuStockEntity
	var spuSkuAttrArr []entity.SpuSkuAttrValueEntity
	for k, v := range attrValueMap {
		stockEntity := entity.SkuStockEntity{}
		stockEntity.SkuId = k.SkuId
		stockEntity.Stock = k.Stock
		stockEntity.ActualStock = k.Stock
		stockEntity.CreateTime = time.Now()
		stockEntity.UpdateTime = time.Now()
		skuStoreArr = append(skuStoreArr, stockEntity)
		if len(v) > 0 {
			for _, v := range v {
				valueEntity := entity.SpuSkuAttrValueEntity{}
				valueEntity.SkuId = k.SkuId
				valueEntity.CreateTime = time.Now()
				valueEntity.UpdateTime = time.Now()
				valueEntity.SpuId = in.SpuId
				valueEntity.AttrId = v.AttrId
				valueEntity.AttrName = v.AttrName
				valueEntity.AttrValueId = v.AttrValueId
				valueEntity.AttrValueName = v.AttrValueName
				valueEntity.Status = 1
				spuSkuAttrArr = append(spuSkuAttrArr, valueEntity)
			}
		}
	}
	return skuStoreArr, spuSkuAttrArr
}

func (l *SaveSkuLogic) skuBatchInsert(in *pb.SaveSkuDTO, skuMapper *mapper.SkuMapper) (
	map[*entity.SkuEntity][]*pb.SpuSkuAttrValueDTO, []int64, error) {
	var skuEntityArr []*entity.SkuEntity
	attrValueMap := make(map[*entity.SkuEntity][]*pb.SpuSkuAttrValueDTO)
	for _, v := range in.SkuArr {
		skuEntity := new(entity.SkuEntity)
		skuEntity.SkuName = v.SkuName
		skuEntity.SpuId = in.SpuId
		skuEntity.CreateTime = time.Now()
		skuEntity.UpdateTime = time.Now()
		skuEntity.Attrs = v.Attrs
		skuEntity.ImgUrl = v.ImgUrl
		skuEntity.PriceFee = v.PriceFee
		skuEntity.MarketPriceFee = v.MarketPriceFee
		skuEntity.PartyCode = v.PartyCode
		skuEntity.ModelId = v.ModelId
		skuEntity.Weight = float64(v.Weight)
		skuEntity.Volume = float64(v.Volume)
		skuEntity.Status = 1
		skuEntity.Stock = int64(v.Stock)
		skuEntityArr = append(skuEntityArr, skuEntity)
		attrValueMap[skuEntity] = v.SpuSkuAttrValues
	}
	skuIdArr, skuBatchInsertErr := skuMapper.BatchInsert(l.ctx, l.svcCtx.DBEngin, skuEntityArr)
	if skuBatchInsertErr != nil {
		return nil, nil, skuBatchInsertErr
	}
	return attrValueMap, skuIdArr, nil
}
